const { Tokenizer } = require("./Tokenizer");

class Parser {
  constructor() {
    this._tokenizer = new Tokenizer();
  }

  parse(string) {
    this._string = string;
    this._tokenizer.init(string);
    this._lookahead = this._tokenizer.getNextToken();

    return this.Program();
  }

  /**
   * Main entry point
   *
   * Program
   *  : Literal
   *  ;
   */
  Program() {
    return {
      type: "Program",
      body: this.Literal(),
    };
  }

  /**
   * Literal
   *  : NumbericLiteral
   *  | StringLiteral
   *  ;
   */
  Literal() {
    switch (this._lookahead.type) {
      case "NUMBER":
        return this.NumbericListeral();
      case "STRING":
        return this.StringLiteral();
    }
    throw new SyntaxError(`Literal: unexpected literal production`);
  }

  /**
   * StringLiteral:
   *  : STRING
   *  ;
   */
  StringLiteral() {
    const token = this._eat("STRING");

    return {
      type: "StringLiteral",
      value: token.value.slice(1, -1),
    };
  }

  /**
   * NumbericLiteral
   *  : NUMBER
   *  ;
   */
  NumbericListeral() {
    const token = this._eat("NUMBER");

    return {
      type: "NumbericLiteral",
      value: Number(token.value),
    };
  }

  _eat(tokenType) {
    const token = this._lookahead;
    if (token == null) {
      throw new SyntaxError(
        `Unexpected end of input, expected: "${tokenType}"`
      );
    }

    if (token.type !== tokenType) {
      throw new SyntaxError(
        `Unexpected token: "${token.value}", expected: "${tokenType}"`
      );
    }

    this._lookahead = this._tokenizer.getNextToken();

    return token;
  }
}

module.exports = {
  Parser,
};
